权限组件


只要继承了 APIView 就可以使用权限组件

组件的执行顺序:  认证 -> 权限 -> 频率

1. 权限类的创建

  • 权限类的固定写法一 -> 没有继承 BasePermission 权限类

    • 如果没有继承 BasePermission 权限类,那么就需要在权限类中编写两个方法

      • has_permission -> 编写权限的逻辑代码
      • has_object_permission -> 不知道有啥用,反正就是要写上 rest-framework 的源码规定的,否则就会报错 

    • 注意:

      • has_permission 方法必须返回True(也可以是能代表True的元素,即: 字符串、列表、字典等)或则False(也可以是能代表True的元素,即: 0、None等),因为是 rest-framework 的源码规定的

# rf_permission.py

class 权限类的类名(object):

    message = '错误信息' # 配置错误信息,如果不配置会使用默认的错误信息

    def has_permission(self, request, view):

 # 权限的逻辑代码

        if True / False:
            return True
        else:
            return False

    def has_object_permission(self, request, view, obj):

        return True

  • 权限类的固定写法二 -> 继承 BasePermission 权限类 -> 常用

    • BasePermission 权限类其实就是一个初始化好的权限类

    • BasePermission 权限类里面也有 has_permission 和 has_object_permission 方法,只不过这两个方法里面没有任何逻辑代码

    • 在编写权限类的时候可以继承 BasePermission 权限类,然后重写 has_permission 方法,而自己编写的权限类就无需写 has_object_permission 方法了,因为 BasePermission 权限类里面有

    • 注意:

      • has_permission 方法必须返回True(也可以是能代表True的元素,即: 字符串、列表、字典等)或则False(也可以是能代表True的元素,即: 0、None等),因为是 rest-framework 的源码规定的

# rf_permission.py

from rest_framework.permissions import BasePermission


class 权限类的类名(BasePermission):

    message = '错误信息' # 配置错误信息,如果不配置会使用默认的错误信息

    def has_permission(self, request, view):

 # 权限的逻辑代码

        if True / False:
            return True
        else:
            return False

  • 权限类的例子

# rf_permission.py

from rest_framework.permissions import BasePermission


class SVIPPermission(BasePermission):

    message = "SVIP才能访问!"  # 配置错误信息,如果不配置会使用默认的错误信息

    def has_permission(self, request, view):
 """
        :param request: APIView 的 request
        :param view: 视图类的实例化对象 -> APIView 类中的 self
        :return: True/False
        """

        # request.user / request.auth 可以使用是因为先执行了认证类然后再执行权限类,且在执行认证类的authenticate方法的时候已经把返回值分别赋值给 request.user / request.auth

        if request.user.user_type == 3:
            print(request.user)  # 用户的数据对象
            print(request.auth)  # token: 7a06fa70c1516f8cd9399e17285d9903
            return True
        else:
            return False

2. 局部权限

  • 局部权限的配置(即: permission_classes = [权限类, 权限类])会覆盖掉该视图类在全局权限中的配置

  • permission_classes= [权限类, 权限类]

# rf_permission.py

from rest_framework.permissions import BasePermission


class SVIPPermission(BasePermission):

    message = "SVIP才能访问!"  # 配置错误信息,如果不配置会使用默认的错误信息

    def has_permission(self, request, view):
 """
        :param request: APIView 的 request
        :param view: 视图类的实例化对象 -> APIView 类中的 self
        :return: True/False
        """

        # request.user / request.auth 可以使用是因为先执行了认证类然后再执行权限类,且在执行认证类的authenticate方法的时候已经把返回值分别赋值给 request.user / request.auth

        if request.user.user_type == 3:
            print(request.user)  # 用户的数据对象
            print(request.auth)  # token: 7a06fa70c1516f8cd9399e17285d9903
            return True
        else:
            return False

# views.py

from .models import *
from .serializer import *
from .rf_auth import *
from .rf_permission import *

from rest_framework import viewsets


class BookViewSet(viewsets.ModelViewSet):
    authentication_classes = [TokenAuth]  # 局部认证,只作用于当前视图
permission_classes = [SVIPPermission]  # 局部权限,只作用于当前视图

    queryset = Book.objects.all()
    serializer_class = BookSerializers

3. 全局权限

  • 全局权限作用于所有视图类

# rf_permission.py

from rest_framework.permissions import BasePermission


class SVIPPermission(BasePermission):

    message = "SVIP才能访问!"  # 配置错误信息,如果不配置会使用默认的错误信息

    def has_permission(self, request, view):
 """
        :param request: APIView 的 request
        :param view: 视图类的实例化对象 -> APIView 类中的 self
        :return: True/False
        """

        # request.user / request.auth 可以使用是因为先执行了认证类然后再执行权限类,且在执行认证类的authenticate方法的时候已经把返回值分别赋值给 request.user / request.auth

        if request.user.user_type == 3:
            print(request.user)  # 用户的数据对象
            print(request.auth)  # token: 7a06fa70c1516f8cd9399e17285d9903
            return True
        else:
            return False

# settings.py

# 配置全局权限所需要的权限类的路径
REST_FRAMEWORK = {
# "DEFAULT_AUTHENTICATION_CLASSES": ["app01.rf_auth.TokenAuth"],  # 全局认证
    "DEFAULT_PERMISSION_CLASSES": ["app01.rf_permission.SVIPPermission"]  # 全局权限
}

  • 不进行权限认证的视图类设置

    • 如果设置了全局权限,但是又想某些视图不进行权限认证,那么可以在不想进行权限认证的视图类中设置 permission_classes= [],因为在 rest-framework 源码中 局部权限的配置 会覆盖掉 全局权限的配置

# views.py

import hashlib
import time

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *
from .rf_auth import *

from rest_framework import viewsets


class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializers


class PublishViewSet(viewsets.ModelViewSet):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializers


# 获取以用户名作为盐,以当前时间作为加密内容的字符串token
def get_token_str(username):
    now_time = str(time.time())
    md5 = hashlib.md5(bytes(username, encoding="utf8"))  # 加盐,使用用户名作为盐,保证token的唯一性
    md5.update(bytes(now_time, encoding='utf-8'))
    return md5.hexdigest()


# 登陆,并且返回当前用户的token
class LoginView(APIView):
permission_classes = [] # 在全局权限下,当前视图不进行权限认证

    def post(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        ret = {
            'status': 0,
            'msg': None
        }
        if user:
            token_str = get_token_str(user.username)  # 获取Token值
# 每次登陆成功后都要修改当前用户的token值
            Token.objects.update_or_create(user=user, defaults={'token': token_str})  # 如果有 user=user 这条数据,那么就修改 token 值,否则就进行添加
            ret['status'] = 1
            ret['token'] = token_str
        else:
            ret['msg'] = '登陆失败'

        return Response(ret)